using System;
using System.Reflection;
using System.Reflection.Emit;

namespace NForth3
{
    class Dictionary
    {
        private NForth nforth;
        public Type ClassType { set; get; }
        public FieldInfo LatestField { set; get; }

        public Dictionary(NForth nforth)
        {
            this.nforth = nforth;
            CreateType();

            // initialize dictionary
            ClassType.GetMethod("Init").Invoke(null, null);
        }

        private void CreateType()
        {
            ModuleBuilder module = nforth.Assembly.DefineDynamicModule("Dictionary");
			TypeBuilder type = module.DefineType("Dictionary", TypeAttributes.Public);

            // fields
            FieldBuilder latest_field = type.DefineField("Latest", nforth.Word.ClassType, FieldAttributes.Static | FieldAttributes.Public);

            // init method
            MethodBuilder init = type.DefineMethod("Init", MethodAttributes.Public | MethodAttributes.Static);
            ILGenerator il = init.GetILGenerator();
            il.Emit(OpCodes.Ldnull);
            il.Emit(OpCodes.Stsfld, latest_field);
            il.Emit(OpCodes.Ret);

            // add method
            MethodBuilder add = type.DefineMethod("Add", MethodAttributes.Public | MethodAttributes.Static, null, new Type[] { nforth.Word.ClassType });
            il = add.GetILGenerator();
            il.Emit(OpCodes.Ldsfld, latest_field);
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Stsfld, nforth.Word.PreviousField);
            il.Emit(OpCodes.Ret);

            type.CreateType();
            module.CreateGlobalFunctions();

            ClassType = module.GetType("Dictionary");
            LatestField = ClassType.GetField("Latest");
        }

        public void Add(string name, int flags, Type type)
        {
            ConstructorInfo word_cons = nforth.Word.ClassType.GetConstructor(
                new Type[] { typeof(string), typeof(int), typeof(Type), nforth.Word.ClassType });
            object word = word_cons.Invoke(new object[] { name, flags, type, null });
            LatestField.SetValue(null, word);
        }
    }
}
